﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Navigation;
using Coding4Fun.Toolkit.Controls;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Threading.Tasks;
using Windows.Networking;
using Windows.Networking.Proximity;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Coding4Fun.PanoHead.Resources;

/*
 * Using artwork from http://openclipart.org/detail/173435/camera-by-jammi-evil-173435 for the icons
 * 
 * */


namespace Coding4Fun.PanoHead
{
    public partial class MainPage : PhoneApplicationPage
    {
        readonly PanoControl _panoControl;

        // Constructor
        public MainPage()
        {
            InitializeComponent();

            ApplicationBar = new ApplicationBar { Mode = ApplicationBarMode.Default, Opacity = 1.0, IsVisible = true };

            CreateConnectAppBar();

            _panoControl = new PanoControl();

            // Set the data context of the listbox control to the sample data
            DataContext = App.panoViewModel;
        }

        private void ResetAppBar()
        {
            ApplicationBar.Buttons.Clear();
            ApplicationBar.MenuItems.Clear();
        }

        private void CreateConnectAppBar()
        {
            ResetAppBar();

            AddAppButton("/Assets/Images/Wireless-Modem.png", AppResources.Connect, buttonConnect_Click);
			AddMenuItem(AppResources.About, about_Click);
        }

		void about_Click(object sender, EventArgs e)
		{
			var about = new AboutPrompt();
			about.Show("Chris Miller", "@anotherlab", "chris@rajapet.net", "http://anotherlab.rajapet.net/");
		}

        private void CreateConnectedAppBar()
        {
            ResetAppBar();

            AddAppButton("/Assets/Images/Camera1.png", AppResources.TakePicture, buttonCamera_Click);
            AddAppButton("/Assets/Images/ArrowHead-Left.png", AppResources.Left, buttonLeft_Click);
            AddAppButton("/Assets/Images/Cancel.png", AppResources.Stop, buttonCancel_Click);
            AddAppButton("/Assets/Images/ArrowHead-Right.png", AppResources.Right, buttonRight_Click);

			AddMenuItem(AppResources.Disconnect, buttonDisconnect_Click);
			AddMenuItem(AppResources.About, about_Click);
        }

        private void AddAppButton(string iconpath, string iconText, EventHandler onClick)
        {
            ApplicationBarIconButton button1 = new ApplicationBarIconButton
                {
                    IconUri = new Uri(iconpath, UriKind.Relative),
                    Text = iconText
                };

            button1.Click += onClick;

            ApplicationBar.Buttons.Add(button1);
        }

        private void AddMenuItem(string name, EventHandler onClick)
        {
			var menu = new ApplicationBarMenuItem(name);
			menu.Click += about_Click;
			ApplicationBar.MenuItems.Add(menu);
        }

        // Load data for the ViewModel Items
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (!App.panoViewModel.IsDataLoaded)
            {
                App.panoViewModel.LoadData();
            }
        }

        private async Task ConnectToLastDevice()
        {
            buttonPano.IsEnabled = false;

            HostName hostname = GetLastUsedHost();
            if (hostname != null)
            {
                await ConnectToDevice(hostname);

                if (_panoControl.IsConnected)
                {
                    buttonPano.IsEnabled = true;
                    CreateConnectedAppBar();
                }
            }
        }

        private async Task<HostName> GetHostName()
        {
            HostName hostname = GetLastUsedHost();

            if (hostname == null)
            {
                hostname = await QueryForHost();
            }

            return hostname;
        }

        private HostName GetLastUsedHost()
        {
            HostName storedHostName = null;

            string storedRawName = App.panoViewModel.AppSettings.LastDevice;

            if (!String.IsNullOrEmpty(storedRawName))
            {
                storedHostName = new HostName(storedRawName);
            }

            return storedHostName;
        }

        /// <summary>
        /// Use the PeerFinder API to locate the Pano Head
        /// </summary>
        /// <returns></returns>
        private async Task<HostName> QueryForHost()
        {
            // Tell PeerFinder to only enumerate paired devices
            // See http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj207007(v=vs.105).aspx#BKMK_Codeexamples
            PeerFinder.AlternateIdentities["Bluetooth:PAIRED"] = "";

			IReadOnlyList<PeerInformation> devices = null;

	        try
	        {
				// Get a list of matching devices
				devices = await PeerFinder.FindAllPeersAsync();
	        }
	        catch{}

            // If we can't find any devices, goto to the phone's Bluetooth settings
            if (devices == null || devices.Count == 0)
            {
                MessageBox.Show(AppResources.NoBlueToothDevices);
                await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings-bluetooth:"));
                return null;
            }

            // Find the first device that identifies as the Pano Head, otherwise go to the Bluetooth settings
            PeerInformation peerInfo = devices.FirstOrDefault(c => c.DisplayName.Contains("Pano Head"));
            if (peerInfo == null)
            {
                MessageBox.Show(AppResources.NoPairedPanos);
                await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings-bluetooth:"));
                return null;
            }

            // Store the device name so the next connection attempt will use that first
            App.panoViewModel.AppSettings.LastDevice = peerInfo.HostName.RawName;
            //saveString("lastdevice", peerInfo.HostName.RawName);

            // Return the hostname
            return peerInfo.HostName;
        }


        private async Task<bool> ConnectToDevice(HostName hostName)
        {
            // Attempt to create a new connection
            StreamSocket s = new StreamSocket();

            popConnect.IsOpen = true;

            try
            {
                // Try to connect to the device on the first port
                await s.ConnectAsync(hostName, "1");

                _panoControl.Socket = s;

                if (_panoControl.IsConnected)
                {
                    WaitForData(s);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            popConnect.IsOpen = false;

            return _panoControl.IsConnected;
        }

        private async void WaitForData(StreamSocket socket)
        {
            try
            {
                DataReader dr = new DataReader(socket.InputStream) {InputStreamOptions = InputStreamOptions.Partial};
                uint numStrBytes = await dr.LoadAsync(512);
                dr.ReadString(numStrBytes);

                if (numStrBytes == 0)
                {
                    MessageBox.Show(string.Format(AppResources.WaitForDisconnectedError, socket.Information.RemoteHostName.DisplayName));
                    return;
                }

                WaitForData(socket);
            }
            catch (Exception ex)
            {
                if (_panoControl.IsConnected)
                {
                    App.panoViewModel.Status = ex.Message;
                }
            }
        }

        private async void buttonCamera_Click(object sender, EventArgs e)
        {
            await _panoControl.TakePicture();
        }

        private async void buttonLeft_Click(object sender, EventArgs e)
        {
            await _panoControl.Left();
        }

        private async void buttonCancel_Click(object sender, EventArgs e)
        {
            await _panoControl.Stop();
        }

        private async void buttonRight_Click(object sender, EventArgs e)
        {
            await _panoControl.Right();
        }

        private async void buttonPano_Click(object sender, RoutedEventArgs e)
        {
            if (_panoControl.IsConnected)
            {
                App.panoViewModel.SaveData();

                await _panoControl.Pano();
            }
            else
            {
                MessageBox.Show(AppResources.PleaseConnectFirst);
            }
        }

        private void buttonDisconnect_Click(object sender, EventArgs e)
        {
            if (_panoControl.IsConnected)
            {
                try
                {
                    _panoControl.CloseSocket(Dispatcher);
                }
                catch (Exception f)
                {
                    MessageBox.Show(String.Format(AppResources.ErrorWhenStopping, f.Message));
                }
            }

            ResetAppBars();
        }

        private async void buttonConnect_Click(object sender, EventArgs e)
        {
            HostName hostname = await GetHostName();

            await ConnectToDevice(hostname);

            ResetAppBars();
        }

        /// <summary>
        /// Resets the app bars based on the current connection status.
        /// </summary>
        private void ResetAppBars()
        {
            if (_panoControl.IsConnected)
            {
                CreateConnectedAppBar();
            }
            else
            {
                CreateConnectAppBar();
            }

            buttonPano.IsEnabled = _panoControl.IsConnected;
        }

        /// <summary>
        /// Try to connect to the last known Bluetooth device.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.</param>
        private async void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
        {
            await ConnectToLastDevice();
        }
    }
}